package gui.GLListeners;

import figuras.BSplines;
import figuras.PuntoVector;
import figuras.malla.MallaContornoSpline;
import figuras.malla.MallaMediaRevolucion;

import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Properties;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.swing.JOptionPane;

import properties.Config;
import transformaciones.TAfin;
import utils.SistemaCoordenadas;
import utils.files.FileManagerFlowers;
import camaras.Camara;

import componentes.flor.Estambre;
import componentes.flor.estambres.Base;
import componentes.flor.estambres.Cabeza;

import constantes.Consts;

public class GLListenerEstambres extends GLListenerCurvas {

	private MallaMediaRevolucion malla;
	private MallaContornoSpline mallaExt;
	// private Estambre estambre;
	private Color colorBase;
	private Color colorCabeza;

	public GLListenerEstambres() {
		super();
		malla = null;
		// estambre=null;
		componente = null;
		colorBase = Color.GREEN;
		colorCabeza = Color.ORANGE;
		this.glOption = GL.GL_POLYGON;
	}

	/***************************************************************************
	 * The init method *
	 * ********************************************************** Call by the
	 * GLDrawable just after the Gl-Context is * initialized. * * This method is
	 * used to : * - activate some modes like texture, light ... * - affect
	 * value to properties * - import testures, load your data ... * *
	 **************************************************************************/
	public void init(GLAutoDrawable glDrawable) {

		// GL gl = glDrawable.getGL();
		gl = glDrawable.getGL();

		LastRot.setIdentity(); // Reset Rotation
		ThisRot.setIdentity(); // Reset Rotation
		ThisRot.get(matrix);

		gl.glClearColor(0.6f, 0.7f, 0.8f, 1.0f);

		// activamos iluminacion
		gl.glEnable(GL.GL_LIGHTING);
		gl.glEnable(GL.GL_LIGHT0);
		// luz0
		gl.glEnable(GL.GL_LIGHT0);
		float LuzDifusa[] = { 1.0f, 1.0f, 1.0f, 1.0f };
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, LuzDifusa, 0);
		float LuzAmbiente[] = { 0.3f, 0.3f, 0.3f, 1.0f };
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, LuzAmbiente, 0);
		float PosicionLuz0[] = new float[4];
		PosicionLuz0[0] = 25.0f;
		PosicionLuz0[1] = 25.0f;
		PosicionLuz0[2] = 0.0f;
		PosicionLuz0[3] = 1.0f;
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, PosicionLuz0, 0);

		// activamos materiales y suavidad
		gl.glEnable(GL.GL_COLOR_MATERIAL);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 0.1f);
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glEnable(GL.GL_NORMALIZE);
		gl.glShadeModel(GL.GL_SMOOTH);

		// volumen de vista
		// N=1; F=1000;
		xRight = Consts.XRIGHT_GLL_ESTAMBRES;
		xLeft = -xRight;
		yTop = xRight;
		yBot = -yTop;

		// Radio del Volumen de Vista =1
		// ClientWidth=400;
		// ClientHeight=400;
		RatioViewPort = 1.0;
		PuntoVector eye = new PuntoVector(15, 15, 15, 1);
		PuntoVector look = new PuntoVector(0, 0, 0, 1);
		PuntoVector up = new PuntoVector(0, 1, 0, 0);
		int flag = 0;// 0 Ortogonal 1 perspectiva
		// if (camara == null)
		camara = new Camara(eye, look, up, flag, xLeft, xRight, yBot, yTop, 1,
				1000, 90, 0.5, gl, glu);

		// File file = new File(Consts.DEFAULT_ESTAMBRES_FILE);
		// if (file.exists())
		// this.cargaEstambre(file);

		// para las texturas
		gl.glEnable(GL.GL_TEXTURE_2D);
	}

	/***************************************************************************
	 * The display event *
	 * ****************************************************************** Here
	 * we place all the code related to the drawing of the scene. * This method
	 * is called by the drawing loop (the display method) * * Gl4java equivalent :
	 * public void drawGlScene() *
	 **************************************************************************/
	public void display(GLAutoDrawable glDrawable) {
		super.display(glDrawable);

		gl.glPointSize(3.0f);
		if (!(malla != null && mallaExt != null)) {
			for (int i = 0; i < manejadorSplines.getPuntosIntroducidos().size(); i++) {
				gl.glBegin(GL.GL_POINTS);
				gl.glVertex2d(manejadorSplines.getPuntosIntroducidos().get(i)
						.getX(), manejadorSplines.getPuntosIntroducidos()
						.get(i).getY());
				gl.glEnd();
			}

			// }
			for (BSplines b : manejadorSplines.getBsplines())
				b.pintaConLineas(gl);
			// b.pintaConPuntos(gl);
		}

		if (componente != null) {
			if (rota) {
				TAfin matriz = new TAfin(gl);
				matriz.setMatriz(matrix);
				componente.setMatriz(matriz);
			}
			componente.dibuja(glOption);
		}

		processEvents(glDrawable);
		setChanged();
		notifyObservers();
	}

	public void limpiaCurvas() {
		super.limpiaCurvas();
		mallaExt = null;
		malla = null;
		// estambre = null;
		componente = null;
	}

	public void setColorBase(Color color) {
		this.colorBase = color;
		Base base = (Base) ((Estambre) componente).getComponentes().get(0);
		if (base != null)
			base.setColor(color);
	}

	public void setColorCabeza(Color color) {
		this.colorCabeza = color;
		Cabeza cabeza = (Cabeza) ((Estambre) componente).getComponentes()
				.get(1);
		if (cabeza != null)
			cabeza.setColor(color);
	}

	public void contorno(GL gl) {
		try {
			// if (malla == null & mallaExt == null) {
			ArrayList<PuntoVector> perfil = this.manejadorSplines.getBsplines()
					.get(0).getPerfil();
			if (perfil.get(0).getY() < perfil.get(perfil.size() - 1).getY()) {
				ArrayList<PuntoVector> control = this.manejadorSplines
						.getBsplines().get(0).getPuntosControl();
				PuntoVector origen = new PuntoVector(0, 0, 0, 1);
				PuntoVector puntoBase = perfil.get(0);
				PuntoVector trasladaOrigen = puntoBase.obtenVector(origen);
				for (PuntoVector p : perfil) {
					p.setX(p.getX() + trasladaOrigen.getX());
					p.setY(p.getY() + trasladaOrigen.getY());
					p.setZ(p.getZ() + trasladaOrigen.getZ());
				}
				for (PuntoVector p : control) {
					p.setX(p.getX() + trasladaOrigen.getX());
					p.setY(p.getY() + trasladaOrigen.getY());
					p.setZ(p.getZ() + trasladaOrigen.getZ());
				}
				Config config = Config.getInstance();
				int lados = config.getInteger("estambre.lados.poligono");
				double grosor = config.getDouble("estambre.grosor");
				mallaExt = new MallaContornoSpline(lados, perfil.size(),
						grosor, this.manejadorSplines.getBsplines().get(0), gl);
				creaCabeza(gl, grosor);
			} else {
				JOptionPane.showMessageDialog(null,
						"Dibuje la base de abajo a arriba", "Vuelva a dibujar",
						JOptionPane.INFORMATION_MESSAGE);
				this.limpiaCurvas();
				camara.frontal();
			}
			// }
		} catch (java.lang.IndexOutOfBoundsException e) {
			JOptionPane.showMessageDialog(null, "Introduzca una spline",
					"Spline no introducida", JOptionPane.WARNING_MESSAGE);
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null, "Error",
					"Error creando estambre", JOptionPane.ERROR_MESSAGE);
			e.printStackTrace();
		}
		// camara.esquina();
		camara.frontal();
		ejeXY = true;
	}

	public void creaCabeza(GL gl, double grosor) {
		ArrayList<PuntoVector> perfil = this.manejadorSplines.getBsplines()
				.get(1).getPerfil();
		PuntoVector p1 = perfil.get(0);
		PuntoVector p2 = perfil.get(perfil.size() - 1);
		if (p1.getX() < p2.getX()) {
			PuntoVector v = p1.obtenVector(p2);
			PuntoVector u = v.perpendicularIzquierda();
			PuntoVector c = p1;
			double factor = grosor/1.8;
			this.manejadorSplines.getBsplines().get(1).setPuntosControl(
					SistemaCoordenadas.changePointsCoordinateSystemEscala(
							this.manejadorSplines.getBsplines().get(1)
									.getPuntosControl(), u, v, c,
							((3 * xRight) / 5)*factor));
			try {
				Config config = Config.getInstance();
				double radianes = // Consts.RADIANES_MEDIA_REVOLUCION;
				config.getDouble("estambre.radianes.mediaRevolucion");
				int pasos = config.getInteger("estambre.pasos.mediaRevolucion");
				malla = new MallaMediaRevolucion(this.manejadorSplines
						.getBsplines().get(1).getPuntosControl(), radianes,
						pasos);
				for (BSplines b : manejadorSplines.getBsplines()) {
					b.perfilaSpline();
				}
				Base base = new Base(mallaExt, this.manejadorSplines
						.getBsplines(), gl);
				base.setColor(colorBase);
				Cabeza cabeza = new Cabeza(malla, this.manejadorSplines
						.getBsplines(), gl);
				cabeza.setColor(colorCabeza);
				componente = new Estambre(base, cabeza, this.manejadorSplines
						.getBsplines(), gl);
				((Estambre) componente).colocaCabeza();
			} catch (IOException e) {
				JOptionPane.showMessageDialog(null, "Error",
						"Error creando estambre", JOptionPane.ERROR_MESSAGE);
				e.printStackTrace();
			}
		} else {
			JOptionPane.showMessageDialog(null,
					"Dibuje la cabeza de izquierda a derecha",
					"Vuelva a dibujar", JOptionPane.INFORMATION_MESSAGE);
			this.limpiaCurvas();
		}
	}

	public void guardaEstambre(File file) {
		try {
			if (componente != null) {
				((Estambre) componente).guardar(file);
			}
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(null, "Archivo no encontrado",
					"Error", JOptionPane.ERROR_MESSAGE);
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null, "Error al guardar", "Error",
					JOptionPane.ERROR_MESSAGE);
		} catch (java.lang.IndexOutOfBoundsException e) {
			JOptionPane.showMessageDialog(null, "Introduzca una spline",
					"Spline no introducida", JOptionPane.WARNING_MESSAGE);
		} finally {
			ejeXY = true;
			camara.frontal();
		}
	}

	public void cargaEstambre(File file, double grosor) {
		try {
			componente = new Estambre(gl, file, xRight, grosor);
			this.manejadorSplines.setBsplines(componente.getBsplines());
			malla = (MallaMediaRevolucion) ((Estambre) componente)
					.getComponentes().get(1).getMalla();
			mallaExt = (MallaContornoSpline) ((Estambre) componente)
					.getComponentes().get(0).getMalla();
			this.colorBase = ((Estambre) componente).getComponentes().get(0)
					.getColor();
			this.colorCabeza = ((Estambre) componente).getComponentes().get(1)
					.getColor();
			camara.esquina();
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(null, "Archivo no encontrado",
					"Error", JOptionPane.ERROR_MESSAGE);
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null, "Error al cargar", "Error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	public void save(String path) {
		super.save(path);
		this.saveGrosor(path);
		if (texturePath != null) {
			FileManagerFlowers.copyFile(texturePath, path
					+ texturePath.substring(texturePath.indexOf("."),
							texturePath.length()));
		}
		// ((Estambre)componente).save(path);
		this.guardaEstambre(new File(path));
	}

	public void saveToAsciiPLY(String filePath) {
		if (componente != null) {
			((Estambre) componente).saveToAsciiPLY(filePath, componente
					.getClass().toString());
		}
	}

	public void saveToBinaryPLY(String filePath) {
		if (componente != null) {
			((Estambre) componente).saveToBinaryPLY(filePath, componente
					.getClass().toString());
		}
	}

	public void setTexture() {
		// ((Base)((Estambre)componente).getComponentes().get(0)).setTexture(texture);
		((Estambre) componente).setTexture(texture);
	}

	private void saveGrosor(String path){
		Properties properties = new Properties();
		InputStreamReader input;
		try {
			input = new InputStreamReader(new FileInputStream(new File(path+".properties")));
			properties.load(input);
			properties.put("grosor", Config.getInstance().getString("estambre.grosor"));
			properties.store(new OutputStreamWriter(new FileOutputStream(path+".properties")), "");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}
